home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 May: Tool Chest / Dev.CD May 97 TC.toast / Sample Code / Overview / CPlusTESample / TEDocument.cp < prev    next >
Encoding:
Text File  |  1994-11-18  |  22.6 KB  |  862 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------------------
  2.  
  3.     Program:    CPlusTESample 2.0
  4.     File:        TEDocument.cp
  5.     Uses:       TEDocument.h
  6.                 TESample.h
  7.  
  8.     by Andrew Shebanow
  9.     of Apple Macintosh Developer Technical Support
  10.  
  11.     Copyright © 1989-1990 Apple Computer, Inc.
  12.     All rights reserved.
  13.  
  14. ------------------------------------------------------------------------------------------*/
  15.  
  16. // Mac Includes
  17. #ifndef __TYPES__
  18. #include <Types.h>
  19. #endif
  20. #ifndef __QUICKDRAW__
  21. #include <QuickDraw.h>
  22. #endif
  23. #ifndef __FONTS__
  24. #include <Fonts.h>
  25. #endif
  26. #ifndef __EVENTS__
  27. #include <Events.h>
  28. #endif
  29. #ifndef __CONTROLS__
  30. #include <Controls.h>
  31. #endif
  32. #ifndef __WINDOWS__
  33. #include <Windows.h>
  34. #endif
  35. #ifndef __MENUS__
  36. #include <Menus.h>
  37. #endif
  38. #ifndef __TEXTEDIT__
  39. #include <TextEdit.h>
  40. #endif
  41. #ifndef __DIALOGS__
  42. #include <Dialogs.h>
  43. #endif
  44. #ifndef __MENUS__
  45. #include <Menus.h>
  46. #endif
  47. #ifndef __DEVICES__
  48. #include <Devices.h>
  49. #endif
  50. #ifndef __SCRAP__
  51. #include <Scrap.h>
  52. #endif
  53. #ifndef __TOOLUTILS__
  54. #include <ToolUtils.h>
  55. #endif
  56. #ifndef __MEMORY__
  57. #include <Memory.h>
  58. #endif
  59. #ifndef __SEGLOAD__
  60. #include <SegLoad.h>
  61. #endif
  62. #ifndef __FILES__
  63. #include <Files.h>
  64. #endif
  65. #ifndef __OSUTILS__
  66. #include <OSUtils.h>
  67. #endif
  68. #ifndef __TRAPS__
  69. #include <Traps.h>
  70. #endif
  71. #ifndef __PACKAGES__
  72. #include <Packages.h>
  73. #endif
  74. #ifndef __ERRORS__
  75. #include <Errors.h>
  76. #endif
  77.  
  78. #ifndef __TEDOCUMENT__
  79. #include "TEDocument.h"
  80. #endif
  81. #ifndef __TESAMPLE__
  82. #include "TESample.h"
  83. #endif
  84.  
  85. // prototypes for functions that can't belong to TEDocument, but
  86. // which are closely tied into our documents
  87. pascal TEClickLoopUPP GetOldClickLoop();
  88. pascal void PascalClickLoop();
  89. pascal void CommonAction(ControlHandle control,short* amount);
  90. pascal void VActionProc(ControlHandle control,short part);
  91. pascal void HActionProc(ControlHandle control,short part);
  92. // this routine is written in Assembler, since it needs to tweak registers
  93. pascal void ASMCLICKLOOP();
  94.  
  95. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  96. const short kTextMargin = 2;
  97.  
  98. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  99. // destination rectangle so that word wrap and horizontal scrolling can be
  100. // demonstrated.
  101. const short    kMaxDocWidth = 576;
  102.  
  103. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  104. // is called.
  105. const short    kMinDocDim = 64;
  106.  
  107. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  108. // so that various errors won't occur from too many characters being in the text.
  109. const short    kMaxTELength = 32000;
  110.  
  111. // kControlInvisible is used the same way to 'turn on' the control.
  112. const short kControlVisible = 0xFF;
  113.  
  114. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  115. // values for control positioning and sizing.
  116. const short kScrollbarAdjust = 15;
  117. const short kGrowboxAdjust = 15;
  118. const short kScrollbarWidth = 16;
  119.  
  120. // kTESlop provides some extra security when pre-flighting edit commands.
  121. const short kTESlop = 1024;
  122.  
  123. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  124. // to have borders coincide with the growbox.
  125. const short kScrollTweek = 2;
  126.  
  127. // kCrChar is used to match with a carriage return when calculating the
  128. // number of lines in the TextEdit record. kDelChar is used to check for
  129. // delete in keyDowns.
  130. const short kCrChar = 13;
  131. const short kDelChar = 8;
  132.  
  133. // notice that we pass the resID parameter up to our base class,
  134. // which actually creates the window for us
  135. TEDocument::TEDocument(short resID)    : TDocument(resID, kTEFileType)
  136. {
  137.     Boolean good;
  138.     Rect destRect, viewRect;
  139.  
  140.     good = false;
  141.     SetPort(fDocWindow);
  142.     GetTERect(&viewRect);
  143.     destRect = viewRect;
  144.     destRect.right = destRect.left + kMaxDocWidth;
  145.     fDocTE = TENew(&destRect, &viewRect);
  146.     FailNIL(fDocTE);
  147.  
  148.     // set up TE record
  149.     AdjustViewRect();
  150.     TEAutoView(true, fDocTE);
  151.     
  152.     // save and replace clickLoop
  153.     fDocClick = (*fDocTE)->clickLoop;
  154.     TEClickLoopUPP upp = NewTEClickLoopProc(&ASMCLICKLOOP);
  155.     (*fDocTE)->clickLoop = upp;
  156.  
  157.     fDocVScroll = fDocHScroll = nil;
  158.  
  159.     // get vertical scrollbar
  160.     TRY
  161.       {
  162.         fDocVScroll = GetNewControl(rVScroll, fDocWindow);
  163.         FailNILResource(fDocVScroll);
  164.         fDocHScroll = GetNewControl(rHScroll, fDocWindow);
  165.         FailNILResource(fDocHScroll);
  166.       }
  167.     RECOVER
  168.       {
  169.         TEDispose(fDocTE);
  170.         if (fDocVScroll)
  171.           delete fDocVScroll;
  172.         if (fDocHScroll)
  173.           delete fDocHScroll;
  174.         FailNewMessage(eNoWindow,gFailMessage,kTEDocErrStrings);
  175.       }
  176.     ENDTRY
  177.  
  178.     AdjustScrollValues(true);
  179.     ShowWindow(fDocWindow);
  180.     SelectWindow(fDocWindow);
  181. }
  182.  
  183. TEDocument::~TEDocument()
  184. {
  185.     HideWindow(fDocWindow);
  186.     if ( fDocTE != nil )
  187.       {
  188.           DisposeRoutineDescriptor((*fDocTE)->clickLoop);
  189.         (*fDocTE)->clickLoop = NULL;
  190.         TEDispose(fDocTE);            // dispose the TEHandle if we got far enough to make one
  191.       }
  192.     if ( fDocVScroll != nil )
  193.       {
  194.         DisposeControl(fDocVScroll);
  195.       }
  196.     if ( fDocHScroll != nil )
  197.       {
  198.         DisposeControl(fDocHScroll);
  199.       }
  200.     // base class destructor will dispose of window
  201. }
  202.  
  203. void TEDocument::DoZoom(short partCode)
  204. {
  205.     Rect tRect;
  206.  
  207.     tRect = fDocWindow->portRect;
  208.     EraseRect(&tRect);
  209.     ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
  210.     AdjustScrollbars(true);        // adjust, redraw anyway
  211.     AdjustTE();
  212.     InvalRect(&tRect);            // invalidate the whole content
  213.     // the scrollbars were taken care of by AdjustScrollbars, so validate ’em
  214.     tRect = (*fDocVScroll)->contrlRect;
  215.     ValidRect(&tRect);
  216.     tRect = (*fDocHScroll)->contrlRect;
  217.     ValidRect(&tRect);
  218. }
  219.  
  220. // Called when a mouseDown occurs in the grow box of an active window.
  221.  
  222. void TEDocument::DoGrow(EventRecord* theEvent)
  223. {
  224.     long growResult;
  225.     Rect tRect, tRect2;
  226.  
  227.     tRect = qd.screenBits.bounds;
  228.     tRect.left = kMinDocDim;
  229.     tRect.top = kMinDocDim;
  230.     growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  231.     // see if it really changed size
  232.     if ( growResult != 0 )
  233.       {
  234.         tRect = (*fDocTE)->viewRect;
  235.         SizeWindow(fDocWindow, LoWord(growResult), HiWord(growResult), true);
  236.         AdjustScrollbars(true);
  237.         AdjustTE();
  238.         // calculate & validate the region that hasn’t changed so it won’t get redrawn
  239.         // Note: we copy rectangles so that we don't take address of object fields.
  240.         tRect2 = (*fDocTE)->viewRect;
  241.         (void) SectRect(&tRect, &tRect2, &tRect);
  242.         tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  243.         ValidRect(&tRect);
  244.         tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  245.         tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  246.       }
  247. }
  248.  
  249. void TEDocument::DoContent(EventRecord* theEvent)
  250. {
  251.     Point mouse;
  252.     ControlHandle control;
  253.     short part, value;
  254.     Boolean shiftDown;
  255.     Rect teRect;
  256.  
  257.     SetPort(fDocWindow);
  258.     mouse = theEvent->where;                            // get the click position
  259.     GlobalToLocal(&mouse);
  260.     GetTERect(&teRect);
  261.     if ( PtInRect(mouse, &teRect) )
  262.       {
  263.         // see if we need to extend the selection
  264.         shiftDown = (theEvent->modifiers & shiftKey) != 0;    // extend if Shift is down
  265.         TEClick(mouse, shiftDown, fDocTE);
  266.       }
  267.     else
  268.       {
  269.         part = FindControl(mouse, fDocWindow, &control);
  270.         switch ( part )
  271.           {
  272.             case 0:
  273.                 // do nothing if not in a control
  274.                 break;
  275.             case inThumb:
  276.                 value = GetControlValue(control);
  277.                 part = TrackControl(control, mouse, nil);
  278.                 if ( part != 0 )
  279.                   {
  280.                     value -= GetControlValue(control);
  281.                     // value now has CHANGE in value; if value changed, scroll
  282.                     if ( value != 0 )
  283.                         if ( control == fDocVScroll )
  284.                             TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  285.                         else TEScroll(value, 0, fDocTE);
  286.                   }
  287.                 break;
  288.             default:                        // they clicked in an arrow, so track & scroll
  289.                 {
  290.                     ControlActionUPP upp;
  291.                     if ( control == fDocVScroll )
  292.                         upp = NewControlActionProc(VActionProc);
  293.                     else
  294.                         upp = NewControlActionProc(HActionProc);
  295.                     value = TrackControl(control, mouse, upp);
  296.                     DisposeRoutineDescriptor(upp);
  297.                 }
  298.                 break;
  299.           }
  300.       }
  301. }
  302.  
  303. void TEDocument::DoKeyDown(EventRecord* theEvent)
  304. {
  305.     char key;
  306.  
  307.     if (theEvent->modifiers & cmdKey)    // don't process command characters
  308.       return;
  309.     key = (char) (theEvent->message & charCodeMask);
  310.     // we have a char. for our window; see if we are still below TextEdit’s
  311.     // limit for the number of characters
  312.     if ((key != kDelChar) &&
  313.         ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 >= kMaxTELength) )
  314.       Failure(eExceedChar,kTEDocErrStrings);
  315.  
  316.     TEKey(key, fDocTE);
  317.     AdjustScrollbars(false);
  318.     AdjustTE();
  319.     fDirty = true;
  320. }
  321.  
  322. void TEDocument::DoActivate(Boolean becomingActive)
  323. {
  324.     if ( becomingActive )
  325.       {
  326.         RgnHandle    tempRgn;
  327.         RgnHandle    clipRgn;
  328.         Rect        growRect;
  329.         Rect        tRect;
  330.  
  331.         // since we don’t want TEActivate to draw a selection in an area where
  332.         // we’re going to erase and redraw, we’ll clip out the update region
  333.         // before calling it.
  334.         tempRgn = NewRgn();
  335.         clipRgn = NewRgn();
  336.         // save old update region
  337.         CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
  338.         // put it in local coords
  339.         OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
  340.         GetClip(clipRgn);
  341.         // subtract updateRgn from clipRgn
  342.         DiffRgn(clipRgn, tempRgn, tempRgn);
  343.         // make it the new clipRgn
  344.         SetClip(tempRgn);
  345.         TEActivate(fDocTE);
  346.         // restore the full-blown clipRgn
  347.         SetClip(clipRgn);
  348.         // get rid of temp regions
  349.         DisposeRgn(tempRgn);
  350.         DisposeRgn(clipRgn);
  351.  
  352.         // the controls must be redrawn on activation:
  353.         (*fDocVScroll)->contrlVis = kControlVisible;
  354.         (*fDocHScroll)->contrlVis = kControlVisible;
  355.         // copy rectangles to avoid unsafe object field references!
  356.         tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
  357.         tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
  358.         // the growbox needs to be redrawn on activation:
  359.         growRect = fDocWindow->portRect;
  360.         // adjust for the scrollbars
  361.         growRect.top = growRect.bottom - kScrollbarAdjust;
  362.         growRect.left = growRect.right - kScrollbarAdjust;
  363.         InvalRect(&growRect);
  364.       }
  365.     else
  366.       {
  367.         TEDeactivate(fDocTE);
  368.         // the controls must be hidden on deactivation:
  369.         HideControl(fDocVScroll);
  370.         HideControl(fDocHScroll);
  371.         // we draw grow icon immediately, since we deactivate controls
  372.         // immediately, and the update delay looks funny
  373.         DrawGrowIcon(fDocWindow);
  374.       }
  375. }
  376.  
  377. void TEDocument::DoUpdate()
  378. {
  379.     BeginUpdate(fDocWindow);                // this sets up the visRgn
  380.     if ( ! EmptyRgn(fDocWindow->visRgn) )    // draw if updating needs to be done
  381.       {
  382.         DrawWindow();
  383.       }
  384.     EndUpdate(fDocWindow);
  385. }
  386.  
  387. // calculate how much idle time we need
  388.  
  389. unsigned long TEDocument::CalcIdle()
  390. {
  391.     if (HaveSelection())
  392.       return GetCaretTime();
  393.     else return kMaxSleepTime;    // if we don't have a selection, we don't need to idle
  394. }
  395.  
  396. // This is called whenever we get a null event et al.
  397. // It takes care of necessary periodic actions. For this program,
  398. // it calls TEIdle.
  399.  
  400. void TEDocument::DoIdle()
  401. {
  402.     TEIdle(fDocTE);
  403. } // DoIdle
  404.  
  405. // Draw the contents of an application window.
  406.  
  407. void TEDocument::DrawWindow()
  408. {
  409.     Rect tRect;
  410.  
  411.     SetPort(fDocWindow);
  412.     tRect = fDocWindow->portRect;
  413.     EraseRect(&tRect);
  414.     TEUpdate(&tRect, fDocTE);
  415.     DrawControls(fDocWindow);
  416.     DrawGrowIcon(fDocWindow);
  417. } // DrawWindow
  418.  
  419. // Return a rectangle that is inset from the portRect by the size of
  420. // the scrollbars and a little extra margin.
  421.  
  422. void TEDocument::GetTERect(Rect* teRect)
  423. {
  424.     *teRect = fDocWindow->portRect;
  425.     InsetRect(teRect, kTextMargin, kTextMargin);            // adjust for margin
  426.     teRect->bottom = teRect->bottom - kScrollbarAdjust;    // and for the scrollbars
  427.     teRect->right = teRect->right - kScrollbarAdjust;
  428. } // GetTERect
  429.  
  430. // setup a region which contains the visible text
  431.  
  432. void TEDocument::GetVisTERgn(RgnHandle rgn)
  433. {
  434.     Rect teRect;
  435.  
  436.     teRect = (*fDocTE)->viewRect;    // get a local copy of viewRect
  437.     SetPort(fDocWindow);            // make sure we have right port
  438.     LocalToGlobal((Point*) &teRect.top);
  439.     LocalToGlobal((Point*) &teRect.bottom);
  440.     RectRgn(rgn, &teRect);
  441.     // we temporarily change the port’s origin to “globalfy” the visRgn
  442.     SetOrigin(-(fDocWindow->portBits.bounds.left),
  443.               -(fDocWindow->portBits.bounds.top));
  444.     SectRgn(rgn, fDocWindow->visRgn, rgn);
  445.     SetOrigin(0, 0);
  446. } // GetTERgn
  447.  
  448. void TEDocument::ReadFromFile(short refNum)
  449. {
  450.     long curPos, size;
  451.     Handle h;
  452.  
  453.     // determine how much data is available to read
  454.     FailOSErr(GetFPos(refNum,&curPos));
  455.     FailOSErr(GetEOF(refNum,&size));
  456.     size -= curPos;
  457.  
  458.     // check for size > 32K
  459.     if (size > kMaxTELength)
  460.       Failure(eExceedChar,kTEDocErrStrings);
  461.  
  462.     // allocate a handle to store it in
  463.     h = NewHandle(size);
  464.     FailNIL(h);
  465.  
  466.     TRY
  467.       {
  468.         FailOSErr(FSRead(refNum,&size,*h));
  469.       }
  470.     RECOVER
  471.       {
  472.         DisposeHandle(h);
  473.       }
  474.     ENDTRY
  475.  
  476.     // now make the text the current text
  477.     SetPort(fDocWindow);
  478.     HLock(h);
  479.     TESetText(*h,size,fDocTE);
  480.     DisposeHandle(h);
  481.  
  482.     // make sure everything is up to date
  483.     Rect tRect = (*fDocTE)->viewRect;
  484.     InvalRect(&tRect);
  485.     AdjustTE();
  486.     AdjustScrollbars(false);
  487. }
  488.  
  489. void TEDocument::WriteToFile(short refNum)
  490. {
  491.     Handle h;
  492.     long size;
  493.  
  494.     // get COPY of TEHandle - doesn't alloc memory
  495.     h = (Handle) TEGetText(fDocTE);
  496.     size = GetHandleSize(h);
  497.     FailOSErr(FSWrite(refNum,&size,*h));
  498. }
  499.  
  500. // Return boolean value indicating that there is or is not a
  501. // selection in the document
  502.  
  503. Boolean TEDocument::HaveSelection()
  504. {
  505.     if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
  506.       return true;
  507.     else return false;
  508. }
  509.  
  510. // Update the TERec's view rect so that it is the greatest multiple of
  511. // the lineHeight that still fits in the old viewRect.
  512.  
  513. void TEDocument::AdjustViewRect()
  514. {
  515.     TEPtr te;
  516.  
  517.     te = *fDocTE;
  518.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  519.                             * te->lineHeight) + te->viewRect.top;
  520. } // AdjustViewRect
  521.  
  522. // Scroll the TERec around to match up to the potentially updated scrollbar
  523. // values. This is really useful when the window has been resized such that the
  524. // scrollbars became inactive but the TERec was already scrolled.
  525.  
  526. void TEDocument::AdjustTE()
  527. {
  528.     TEPtr te;
  529.  
  530.     te = *fDocTE;
  531.     TEScroll((te->viewRect.left - te->destRect.left) - GetControlValue(fDocHScroll),
  532.              (te->viewRect.top - te->destRect.top) -
  533.                  (GetControlValue(fDocVScroll) * te->lineHeight),
  534.              fDocTE);
  535. } // AdjustTE
  536.  
  537. // Re-calculate the position and size of the viewRect and the scrollbars.
  538. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  539. // to have borders coincide with the growbox.
  540.  
  541. void TEDocument::AdjustScrollSizes()
  542. {
  543.     Rect teRect;
  544.  
  545.     GetTERect(&teRect);
  546.     (*fDocTE)->viewRect = teRect;
  547.     AdjustViewRect();
  548.     MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
  549.     SizeControl(fDocVScroll, kScrollbarWidth,
  550.                 fDocWindow->portRect.bottom - fDocWindow->portRect.top -
  551.                     kGrowboxAdjust + kScrollTweek);
  552.     MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  553.     SizeControl(fDocHScroll,
  554.                 fDocWindow->portRect.right - fDocWindow->portRect.left -
  555.                     kGrowboxAdjust + kScrollTweek,
  556.                 kScrollbarWidth);
  557. } // AdjustScrollSizes
  558.  
  559. // Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  560. // and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  561. // then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  562. // by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
  563. // necessary).
  564.  
  565. void TEDocument::AdjustScrollbars(Boolean needsResize)
  566. {
  567.     // First, turn visibility of scrollbars off so we won’t get unwanted redrawing
  568.     (*fDocVScroll)->contrlVis = 0;
  569.     (*fDocHScroll)->contrlVis = 0;
  570.     if ( needsResize )
  571.       AdjustScrollSizes();
  572.     AdjustScrollValues(needsResize);
  573.     // Now, restore visibility in case we never had to draw during adjustment
  574.     (*fDocVScroll)->contrlVis = 0xff;
  575.     (*fDocHScroll)->contrlVis = 0xff;
  576. } // AdjustScrollbars
  577.  
  578. // Calculate the new control maximum value and current value, whether it is the horizontal or
  579. // vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  580. // vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  581. // width to the width of the viewRect. The current values are set by comparing the offset between
  582. // the view and destination rects. If necessary, redraw the control by calling ShowControl.
  583.  
  584. void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
  585. {
  586.     short value, lines, max;
  587.     short oldValue, oldMax;
  588.     TEPtr te;
  589.     ControlHandle control;
  590.  
  591.     if (isVert)
  592.       control = fDocVScroll;
  593.     else control = fDocHScroll;
  594.     oldValue = GetControlValue(control);
  595.     oldMax = GetControlMaximum(control);
  596.     te = *fDocTE;                            // point to TERec for convenience
  597.     if ( isVert )
  598.       {
  599.         lines = te->nLines;
  600.         // since nLines isn’t right if the last character is a return, check for that case
  601.         if ( *(*te->hText + te->teLength - 1) == kCrChar )
  602.           lines += 1;
  603.         max = lines - ((te->viewRect.bottom - te->viewRect.top) /
  604.                        te->lineHeight);
  605.       }
  606.     else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
  607.  
  608.     if ( max < 0 )
  609.       max = 0;
  610.     SetControlMaximum(control, max);
  611.  
  612.     // Must deref. after SetControlMaximum since, technically, it could draw and therefore move
  613.     // memory. This is why we don’t just do it once at the beginning.
  614.     te = *fDocTE;
  615.     if ( isVert )
  616.       value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
  617.     else value = te->viewRect.left - te->destRect.left;
  618.  
  619.     if ( value < 0 )
  620.       value = 0;
  621.     else if ( value >  max )
  622.       value = max;
  623.  
  624.     SetControlValue(control, value);
  625.     // now redraw the control if asked to or if a setting changed
  626.     if ( mustRedraw || (max != oldMax) || (value != oldValue) )
  627.         ShowControl(control);
  628. } // AdjustHV
  629.  
  630. // Simply call the common adjust routine for the vertical and horizontal scrollbars.
  631.  
  632. void TEDocument::AdjustScrollValues(Boolean mustRedraw)
  633. {
  634.     AdjustHV(true, mustRedraw);
  635.     AdjustHV(false, mustRedraw);
  636. } // AdjustScrollValues
  637.  
  638. TEClickLoopUPP TEDocument::GetClickLoop()
  639. {
  640.     return fDocClick;
  641. }
  642.  
  643. TEHandle TEDocument::GetTEHandle()
  644. {
  645.     return fDocTE;
  646. }
  647.  
  648. void TEDocument::DoCut()
  649. {
  650.     long total, contig;
  651.  
  652.     if (ZeroScrap() == noErr)
  653.       {
  654.         PurgeSpace(&total, &contig);
  655.         if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
  656.           Failure(eNoSpaceCut,kTEDocErrStrings);
  657.  
  658.         TECut(fDocTE);
  659.         fDirty = true;
  660.         if (TEToScrap() != noErr)
  661.           {
  662.             (void) ZeroScrap();
  663.             Failure(eNoCut,kTEDocErrStrings);
  664.           }
  665.       }
  666.     AdjustScrollbars(false);
  667.     AdjustTE();
  668. }
  669.  
  670. void TEDocument::DoCopy()
  671. {
  672.     if (ZeroScrap() == noErr)
  673.       {
  674.         TECopy(fDocTE);                // after copying, export the TE scrap
  675.         if (TEToScrap() != noErr)
  676.           {
  677.             ZeroScrap();
  678.             Failure(eNoCopy,kTEDocErrStrings);
  679.           }
  680.       }
  681.     AdjustScrollbars(false);
  682.     AdjustTE();
  683. }
  684.  
  685. void TEDocument::DoPaste()
  686. {
  687.     Handle aHandle;
  688.     long oldSize, newSize;
  689.     OSErr saveErr;
  690.  
  691.     if (TEFromScrap() != noErr)
  692.       Failure(eNoPaste,kTEDocErrStrings);
  693.  
  694.     if ( TEGetScrapLength() + ((*fDocTE)->teLength -
  695.          ((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
  696.       Failure(eExceedPaste,kTEDocErrStrings);
  697.  
  698.     aHandle = (Handle) TEGetText(fDocTE);
  699.     oldSize = GetHandleSize(aHandle);
  700.     newSize = oldSize + TEGetScrapLength() + kTESlop;
  701.  
  702.     // preflight the growth of the text handle for textedit,
  703.     // since it will crash if it doesn't have enough memory
  704.     SetHandleSize(aHandle, newSize);
  705.     saveErr = MemError();
  706.     SetHandleSize(aHandle, oldSize);
  707.     if (saveErr != noErr)
  708.       Failure(eNoSpacePaste,kTEDocErrStrings);
  709.  
  710.     TEPaste(fDocTE);
  711.     fDirty = true;
  712.  
  713.     AdjustScrollbars(false);
  714.     AdjustTE();
  715. }
  716.  
  717. void TEDocument::DoClear()
  718. {
  719.     TEDelete(fDocTE);
  720.     fDirty = true;
  721.     AdjustScrollbars(false);
  722.     AdjustTE();
  723. }
  724.  
  725. void TEDocument::DoSelectAll()
  726. {
  727.     long selSize = (*fDocTE)->teLength;
  728.     TESetSelect(0,selSize,fDocTE);
  729. }
  730.  
  731. /*
  732.     Routines used by this class, which don't belong to the class since we use
  733.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  734. */
  735.  
  736. // we refer back to the owning application so that we can get access to
  737. // the document list, to find the current document object.
  738. extern TESample* gTheApplication;
  739.  
  740. // Common algorithm for pinning the value of a control. It returns the actual amount
  741. // the value of the control changed.
  742.  
  743. pascal void CommonAction(ControlHandle control,short* amount)
  744. {
  745.     short        value, max;
  746.  
  747.     value = GetControlValue(control);
  748.     max = GetControlMaximum(control);
  749.     *amount = value - *amount;
  750.     if ( *amount <= 0 )
  751.         *amount = 0;
  752.     else if ( *amount >= max )
  753.         *amount = max;
  754.     SetControlValue(control, *amount);
  755.     *amount = value - *amount;
  756. } // CommonAction
  757.  
  758.  
  759. // Determines how much to change the value of the vertical scrollbar by and how
  760. // much to scroll the TE record.
  761.  
  762. pascal void VActionProc(ControlHandle control,short part)
  763. {
  764.     short        amount;
  765.     WindowPtr    window;
  766.     TEPtr        te;
  767.     TEDocument* doc;
  768.  
  769.     if ( part != 0 )
  770.       {
  771.         window = (*control)->contrlOwner;
  772.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  773.         te = *(doc->GetTEHandle());
  774.         switch ( part )
  775.           {
  776.             case inUpButton:
  777.             case inDownButton:        // one line
  778.                 amount = 1;
  779.                 break;
  780.             case inPageUp:            // one page
  781.             case inPageDown:
  782.                 amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  783.                 break;
  784.           }
  785.         if ( (part == inDownButton) || (part == inPageDown) )
  786.             amount = -amount;        // reverse direction for a downer
  787.         CommonAction(control, &amount);
  788.         if ( amount != 0 )
  789.             TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
  790.       }
  791. } // VActionProc
  792.  
  793. // Determines how much to change the value of the horizontal scrollbar by and how
  794. // much to scroll the TE record.
  795.  
  796. pascal void HActionProc(ControlHandle control,short part)
  797. {
  798.     short        amount;
  799.     WindowPtr    window;
  800.     TEPtr        te;
  801.     TEDocument* doc;
  802.  
  803.     if ( part != 0 )
  804.       {
  805.         window = (*control)->contrlOwner;
  806.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  807.         te = *(doc->GetTEHandle());
  808.         switch ( part )
  809.           {
  810.             case inUpButton:
  811.             case inDownButton:        // a few pixels
  812.                 amount = 4;
  813.                 break;
  814.             case inPageUp:            // a page
  815.             case inPageDown:
  816.                 amount = te->viewRect.right - te->viewRect.left;
  817.                 break;
  818.           }
  819.         if ( (part == inDownButton) || (part == inPageDown) )
  820.             amount = -amount;        // reverse direction
  821.         CommonAction(control, &amount);
  822.         if ( amount != 0 )
  823.             TEScroll(amount, 0, doc->GetTEHandle());
  824.       }
  825. } // VActionProc
  826.  
  827. // Gets called from our assembly language routine, AsmClickLoop, which is in
  828. // turn called by the TEClick toolbox routine. Saves the windows clip region,
  829. // sets it to the portRect, adjusts the scrollbar values to match the TE scroll
  830. // amount, then restores the clip region.
  831.  
  832. pascal void PascalClickLoop()
  833. {
  834.     RgnHandle    region;
  835.     WindowPtr wind;
  836.     TEDocument* doc;
  837.  
  838.     wind = FrontWindow();
  839.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(wind);
  840.     region = NewRgn();
  841.     GetClip(region);    // save clip
  842.     ClipRect(&wind->portRect);
  843.     doc->AdjustScrollValues(false);
  844.     SetClip(region);    // restore clip
  845.     DisposeRgn(region);
  846. } // PascalClickLoop
  847.  
  848. // Gets called from our assembly language routine, AsmClickLoop, which is in
  849. // turn called by the TEClick toolbox routine. It returns the address of the
  850. // default clickLoop routine that was put into the TERec by TEAutoView to
  851. // AsmClickLoop so that it can call it.
  852.  
  853. pascal TEClickLoopUPP GetOldClickLoop()
  854. {
  855.     TEDocument* doc;
  856.  
  857.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(FrontWindow());
  858.     if (doc == nil)
  859.       return nil;
  860.     return doc->GetClickLoop();
  861. } // GetOldClickLoop
  862.